#pragma once

#include "../common/Log.hpp"
#include "../common/SqliteHelper.hpp"
#include "../common/FileHelper.hpp"
#include "../common/message.pb.h"
#include "MsgQueue.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <cassert>

namespace micromq
{
    class MsgQueueMapper
    {
    public:
        MsgQueueMapper(const std::string& dbFileName): _sqliteHelper(dbFileName)
        {
            std::string parentPath = FileHelper::getParentPath(dbFileName);
            FileHelper::createDir(parentPath);
            assert(_sqliteHelper.open());
            createTable();
        }

        void createTable()
        {
            std::stringstream sql;
            sql << "create table if not exists queue(";
            sql << "    `name`           varchar(128) primary key,    ";
            sql << "    `durable`        int,                         ";
            sql << "    `exclusive`      int,                         ";
            sql << "    `auto_delete`    int,                         ";
            sql << "    `args`           varchar(128)                 ";
            sql << ");";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("create table queue error, abort...");
                ::abort();
            }
        }

        void removeTable()
        {
            std::string sql = "drop table if exists queue;";
            if(!_sqliteHelper.execute(sql, nullptr, nullptr))
            {
                logError("drop table queue error...");
            }
        }

        void insertMsgQueue(MsgQueue::ptr& queue)
        {
            std::stringstream sql;
            sql << "insert into queue values ('";
            sql << queue->_name << "', ";
            sql << queue->_durable << ", ";
            sql << queue->_exclusive << ", ";
            sql << queue->_autoDelete << ", '";
            sql << queue->getArgs() << "'";
            sql << ")";

            if(!_sqliteHelper.execute(sql.str(), nullptr, nullptr))
            {
                logError("insert into queue error...");
            }
        }

        void deleteMsgQueue(const std::string& name)
        {
            std::string sql = "delete from queue where name='" + name + "';";
            if(!_sqliteHelper.execute(sql, nullptr, nullptr))
            {
                logError("delete queue: %s error...", name.c_str());
            }
        }

        std::unordered_map<std::string, MsgQueue::ptr> selectAll()
        {
            std::string sql = "select * from queue;";
            std::unordered_map<std::string, MsgQueue::ptr> result;
            if(!_sqliteHelper.execute(sql, onSelectAll, &result))
            {
                logError("select all from queue error...");
            }
            return result;
        }

        ~MsgQueueMapper()
        {}
    private:
        static int onSelectAll(void* args, int colNum, char** result, char** fieldName)
        {
            auto queueMap = static_cast<std::unordered_map<std::string, MsgQueue::ptr>*>(args);
            auto queue = std::make_shared<MsgQueue>();
            queue->_name = result[0];
            queue->_durable = static_cast<bool>(std::stoi(result[1]));
            queue->_exclusive = static_cast<bool>(std::stoi(result[2]));
            queue->_autoDelete = static_cast<bool>(std::stoi(result[3]));
            if(result[4] != nullptr) queue->setArgs(result[4]);
            queueMap->insert(std::make_pair(queue->_name, queue));
            return 0;
        }
    private:
        SqliteHelper _sqliteHelper;
    };
}